package com.example.sensorapp.Services;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map.Entry;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

public class SensorListenerService extends Service implements SensorEventListener
{
	private SensorManager sManager;
	private Sensor accelerometer;
	private final float POSITIVE_NOISE = 2.0F;
	private final float NEGATIVE_NOISE = -2.0F;
	private ArrayList<Float[]> sensorValues;
	private float mLastX, mLastY, mLastZ, prevAvg;
	
	@Override
	public IBinder onBind(Intent arg0) 
	{
		return null;
	}
	
	@Override
	public int onStartCommand(final Intent intent, int flags, int startId)
	{
		super.onStartCommand(intent, flags, startId);
		
		Toast.makeText(this, "Service started", Toast.LENGTH_SHORT).show();
		
		sensorValues = new ArrayList<Float[]>();
		sManager = (SensorManager) this.getSystemService(Context.SENSOR_SERVICE);
		accelerometer = sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		sManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
		
		mLastX = 0.0F;
		mLastY = 0.0F;
		mLastZ = 0.0F;
		
		return Service.START_STICKY;
	}
	
	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy)
	{
		//Not used
	}
	
	@Override
	public void onSensorChanged(SensorEvent event)
	{
		char axis = getAxis(event);
		float speed = getSpeed(axis, event);
		boolean deviceFalling = isFalling(axis, event);
		
		Log.d("Axis:", Character.toString(axis));
		Log.d("Speed:", Float.toString(speed));
		Log.d("Is Falling:", Boolean.toString(deviceFalling));
		
		float x = event.values[0];
		float y = event.values[1];
		float z = event.values[2];
		
//		if (sensorValues.size() < 3)
//		{
//			Float[] vals = {x, y, z};
//			sensorValues.add(vals);
//		}
//		else if (sensorValues.size() == 3)
//		{
//			float avg = getAverageSpeed();
//			boolean deviceFalling = isFalling(axis, event);
//			
//			if (avg >= 17.0F && prevAvg <= 5.0F && deviceFalling == true)
//			{
//				String phoneNum = "7012003169";
//				String message = "Your HTC device detected a fall";
//				
//				SmsManager sms = SmsManager.getDefault();
//				sms.sendTextMessage(phoneNum, null, message, null, null);
//			}
//			prevAvg = avg;			
//		}
		
		mLastX = x;
		mLastY = y;
		mLastZ = z;
	}

	@Override
	public void onDestroy()
	{
		super.onDestroy();
		
		sensorValues.clear();
		sensorValues = null;
		sManager.unregisterListener(this);
		sManager = null;
		accelerometer = null;
	}
	/**
     * Determines what axis the phone is currently falling on.  Determined by
     * checking the difference between the force of gravity and the current value of the sensor.
     * The closer to zero the difference is, the axis the phone is currently on.
     * @param event
     * @return
     */
    private char getAxis(SensorEvent event)
    {
    	Hashtable<Character, Float> axisTable = new Hashtable<Character, Float>();
    	
    	axisTable.put('x', Math.abs(event.values[0]));
    	axisTable.put('y', Math.abs(event.values[1]));
    	axisTable.put('z', Math.abs(event.values[2]));
    	
    	Iterator<Entry<Character, Float>> it = axisTable.entrySet().iterator();
    	Entry<Character, Float> item = it.next();
    	
    	while (it.hasNext())
    	{
    		float modVal1 = 9.8F - item.getValue();
    		Entry<Character,Float> entry = it.next();
    		float modVal2 = 9.8F - entry.getValue();
    		
    		if (modVal2 < modVal1)
    			item = entry;
    	}
    	
    	return item.getKey();
    }
    /**
     * Gets the speed in which the device is moving by getting the difference of the current value and the last value
     * when the sensor has changed.
     * @param axis
     * @param event
     * @return
     */
    private float getSpeed(char axis, SensorEvent event)
    {
    	float sensorValue = 0.0F;
    	float speed = 0.0F;
    	float difference = 0.0F;
    	
    	switch (axis)
    	{
	    	case 'x':
	    		sensorValue = event.values[0];
	    		difference = Math.abs(mLastX - sensorValue);
	    		break;
	    	case 'y':
	    		sensorValue = event.values[1];
	    		difference = Math.abs(mLastY - sensorValue);
	    		break;
	    	case 'z':
	    		sensorValue = event.values[2];
	    		difference = Math.abs(mLastZ - sensorValue);
	    		break;
    	}
    	
    	if (difference < POSITIVE_NOISE && difference > NEGATIVE_NOISE)
    		difference = 0.0F;
    	
    	speed = difference;
    	
    	return speed;
    }
    /**
     * Determines whether the device is falling by first obtaining the axis that gravity is affecting the most
     * and then finding the difference between the last value and current value.  If the value is negative than
     * the device is most likely falling downward.
     * @param axis
     * @param event
     * @return
     */
    private boolean isFalling(char axis, SensorEvent event)
    {
    	float sensorValue = 0.0F;
    	float difference = 0.0F;
    	boolean isFalling = false;
    	
    	switch (axis)
    	{
	    	case 'x':
	    		sensorValue = event.values[0];
	    		difference = mLastX - sensorValue;
	    		break;
	    	case 'y':
	    		sensorValue = event.values[1];
	    		difference = mLastY - sensorValue;
	    		break;
	    	case 'z':
	    		sensorValue = event.values[2];
	    		difference = mLastZ - sensorValue;
	    		break;
    	}
    	
    	if (difference < POSITIVE_NOISE && difference > NEGATIVE_NOISE)
    		difference = 0.0F;
    	
    	if (difference == 0.0F)
    		isFalling = false;
    	else if (difference > 0.0F)
    		isFalling = false;
    	else if (difference < 0.0F)
    		isFalling = true;
    	
    	return isFalling;
    }
    private float getAverageSpeed()
    {
    	float totalX = 0.0F, totalY = 0.0F, totalZ = 0.0F;
    	float avgX = 0.0F, avgY = 0.0F, avgZ = 0.0F;
    	
    	for (Float[] item : sensorValues)
    	{
    		totalX += item[0];
    		totalY += item[1];
    		totalZ += item[2];
    	}
    	
    	avgX = totalX / sensorValues.size();
    	avgY = totalY / sensorValues.size();
    	avgZ = totalZ / sensorValues.size();
    	
    	float answer = (float) Math.sqrt((Math.pow(avgX, 2) + Math.pow(avgY, 2) + Math.pow(avgZ, 2)));
    	sensorValues.clear();
    	
    	return answer;
    }
}
